package cn.admin.config.common.lang.enumeration;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.MessageFormat;
import java.util.Hashtable;
import java.util.Map;
import java.util.WeakHashMap;

public class EnumUtil {

    private static final Map<ClassLoader, Map<String, Enum.EnumType>> entries = new WeakHashMap<>();

    private static final Object object = new Object();

    public static Enum getEnumByName(Class<?> enumClass, String name) {

        Enum.EnumType enumType = getEnumType(enumClass);
        if (enumType.enumList.size() != enumType.nameMap.size()) {
            enumType.populateNames(enumClass);
        }
        return enumType.nameMap.get(name);
    }

    static Enum.EnumType getEnumType(Class<?> enumClass) {

        if (enumClass == null) {
            throw new NullPointerException("The Enum class must not be null");
        } else {
            synchronized (object) {
                if (!Enum.class.isAssignableFrom(enumClass)) {
                    throw new IllegalArgumentException(MessageFormat.
                            format("Class \"{0}\" is not a subclass of Enum",
                                    enumClass.getName()));
                } else {
                    Map<String, Enum.EnumType> entryMap = getEnumEntryMap(enumClass);
                    Enum.EnumType enumType = entryMap.get(enumClass.getName());
                    if (enumType == null) {
                        Method createEnumTypeMethod = findStaticMethod(enumClass,
                                "createEnumType");
                        if (createEnumTypeMethod != null) {
                            try {
                                enumType = (Enum.EnumType) createEnumTypeMethod.invoke(null);
                            } catch (IllegalAccessException | InvocationTargetException e) {
                                e.printStackTrace();
                            }
                        }

                        if (enumType != null) {
                            entryMap.put(enumClass.getName(), enumType);
                            enumType.populateNames(enumClass);
                        }
                    }

                    if (enumType == null) {
                        throw new UnsupportedOperationException(MessageFormat.
                                format("Could not create EnumType for class \"{0}\"",
                                        enumClass.getName()));
                    } else {
                        return enumType;
                    }
                }
            }
        }
    }

    static Map<String, Enum.EnumType> getEnumEntryMap(Class<?> enumClass) {

        ClassLoader classLoader = enumClass.getClassLoader();
        Map<String, Enum.EnumType> entryMap;

        synchronized (entries) {
            entryMap = entries.get(classLoader);
            if (entryMap == null) {
                entryMap = new Hashtable<>();
                entries.put(classLoader, entryMap);
            }
            return entryMap;
        }

    }

    private static Method findStaticMethod(Class<?> enumClass, String methodName,
                                           Class<?>... paramTypes) {

        Method method = null;
        Class<?> clazz = enumClass;
        while (!clazz.equals(Enum.class)) {
            try {
                method = clazz.getDeclaredMethod(methodName, paramTypes);
                break;
            } catch (NoSuchMethodException e) {
                clazz = clazz.getSuperclass();
            }
        }
        return method != null && Modifier.isStatic(method.getModifiers()) ? method : null;
    }

}
